import pandas as pd
import numpy as np
import dalex as dx
from sklearn.svm import SVC
from sklearn.model_selection import train_test_split
import warnings
from sklearn.preprocessing import StandardScaler, MinMaxScaler
from sklearn.pipeline import Pipeline
warnings.filterwarnings('ignore')
!pip install lime
Collecting lime Using cached lime-0.2.0.1.tar.gz (275 kB) Requirement already satisfied: matplotlib in c:\users\patryk\anaconda3\lib\site-packages (from lime) (3.3.4) Requirement already satisfied: numpy in c:\users\patryk\anaconda3\lib\site-packages (from lime) (1.19.2) Requirement already satisfied: scipy in c:\users\patryk\anaconda3\lib\site-packages (from lime) (1.6.1) Requirement already satisfied: tqdm in c:\users\patryk\anaconda3\lib\site-packages (from lime) (4.58.0) Requirement already satisfied: scikit-learn>=0.18 in c:\users\patryk\anaconda3\lib\site-packages (from lime) (0.24.1) Requirement already satisfied: scikit-image>=0.12 in c:\users\patryk\anaconda3\lib\site-packages (from lime) (0.17.2) Requirement already satisfied: networkx>=2.0 in c:\users\patryk\anaconda3\lib\site-packages (from scikit-image>=0.12->lime) (2.5) Requirement already satisfied: pillow!=7.1.0,!=7.1.1,>=4.3.0 in c:\users\patryk\anaconda3\lib\site-packages (from scikit-image>=0.12->lime) (8.1.2) Requirement already satisfied: imageio>=2.3.0 in c:\users\patryk\anaconda3\lib\site-packages (from scikit-image>=0.12->lime) (2.9.0) Requirement already satisfied: tifffile>=2019.7.26 in c:\users\patryk\anaconda3\lib\site-packages (from scikit-image>=0.12->lime) (2021.3.5) Requirement already satisfied: PyWavelets>=1.1.1 in c:\users\patryk\anaconda3\lib\site-packages (from scikit-image>=0.12->lime) (1.1.1) Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.3 in c:\users\patryk\anaconda3\lib\site-packages (from matplotlib->lime) (2.4.7) Requirement already satisfied: kiwisolver>=1.0.1 in c:\users\patryk\anaconda3\lib\site-packages (from matplotlib->lime) (1.3.1) Requirement already satisfied: python-dateutil>=2.1 in c:\users\patryk\anaconda3\lib\site-packages (from matplotlib->lime) (2.8.1) Requirement already satisfied: cycler>=0.10 in c:\users\patryk\anaconda3\lib\site-packages (from matplotlib->lime) (0.10.0) Requirement already satisfied: six in c:\users\patryk\anaconda3\lib\site-packages (from cycler>=0.10->matplotlib->lime) (1.15.0) Requirement already satisfied: decorator>=4.3.0 in c:\users\patryk\anaconda3\lib\site-packages (from networkx>=2.0->scikit-image>=0.12->lime) (4.4.2) Requirement already satisfied: threadpoolctl>=2.0.0 in c:\users\patryk\anaconda3\lib\site-packages (from scikit-learn>=0.18->lime) (2.1.0) Requirement already satisfied: joblib>=0.11 in c:\users\patryk\anaconda3\lib\site-packages (from scikit-learn>=0.18->lime) (1.0.1) Building wheels for collected packages: lime Building wheel for lime (setup.py): started Building wheel for lime (setup.py): finished with status 'done' Created wheel for lime: filename=lime-0.2.0.1-py3-none-any.whl size=283845 sha256=749e3dad66556a03fde91ec9cfaf74356cc6705d3a9392eeeaaa1cf3a9278de1 Stored in directory: c:\users\patryk\appdata\local\pip\cache\wheels\e6\a6\20\cc1e293fcdb67ede666fed293cb895395e7ecceb4467779546 Successfully built lime Installing collected packages: lime Successfully installed lime-0.2.0.1
df = pd.read_csv('heart_data.csv')
y = df.target.values
x = df.drop(['target'], axis = 1)
x_train, x_test, y_train, y_test = train_test_split(x,y, test_size = 0.2,random_state=0, stratify=y)
ss = StandardScaler()
p_svc = Pipeline(
[
('standardscaler', ss),
('SVC', SVC( C = 0.5, random_state=1, probability=True))
]
)
p_svc.fit(x_train, y_train)
Pipeline(steps=[('standardscaler', StandardScaler()),
('SVC', SVC(C=0.5, probability=True, random_state=1))])
expl = dx.Explainer(p_svc, x_train, y_train, label='SVC')
Preparation of a new explainer is initiated -> data : 242 rows 20 cols -> target variable : 242 values -> model_class : sklearn.svm._classes.SVC (default) -> label : SVC -> predict function : <function yhat_proba_default at 0x000001E257A1AA60> will be used (default) -> predict function : Accepts pandas.DataFrame and numpy.ndarray. -> predicted values : min = 0.0525, mean = 0.446, max = 0.982 -> model type : classification will be used (default) -> residual function : difference between y and yhat (default) -> residuals : min = -0.934, mean = 0.0127, max = 0.915 -> model_info : package sklearn A new explainer has been created!
Mój zbiór danych przedstawia osoby podejrzane o występowanie choroby wieńcowej. Zmienne zawierają wyniki różnych badań, poszczególne z nich zostaną omówione poniżej.
Będę korzystał z modelu SVC ponieważ nasze analizy pokazały, że jest on najlepszy do naszego problemu
df.head()
| age | sex | trestbps | chol | fbs | restecg | thalach | exang | oldpeak | ca | ... | thal_fd | thal_rd | slope_up | slope_flat | slope_down | cp_ta | cp_aa | cp_np | cp_a | target | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 63 | 1 | 145 | 233 | 1 | 1 | 150 | 0 | 2.3 | 0 | ... | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 |
| 1 | 67 | 1 | 160 | 286 | 0 | 1 | 108 | 1 | 1.5 | 3 | ... | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 |
| 2 | 67 | 1 | 120 | 229 | 0 | 1 | 129 | 1 | 2.6 | 2 | ... | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 |
| 3 | 37 | 1 | 130 | 250 | 0 | 0 | 187 | 0 | 3.5 | 0 | ... | 0 | 0 | 0 | 0 | 1 | 0 | 0 | 1 | 0 | 0 |
| 4 | 41 | 0 | 130 | 204 | 0 | 1 | 172 | 0 | 1.4 | 0 | ... | 0 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 | 0 |
5 rows × 21 columns
Wybieramy pierwszą obserwację ze zbioru testowego. Jest to 68 letni mężczyzna z wysokim choresterolem, bólem niedławicowym, poziomem cukru powyżej 120, odwracalną wadą serca.
Ma on chorobę wieńcową. Nasz model również zaklasyfikował go jako chorego.
obs = x_test.iloc[1:2]
y_obs = y_test[1]
obs
| age | sex | trestbps | chol | fbs | restecg | thalach | exang | oldpeak | ca | thal_n | thal_fd | thal_rd | slope_up | slope_flat | slope_down | cp_ta | cp_aa | cp_np | cp_a | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 83 | 68 | 1 | 180 | 274 | 1 | 1 | 150 | 1 | 1.6 | 0 | 0 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 0 |
print('Rzeczywista wartość targetu: ' + str(y_obs))
Rzeczywista wartość targetu: 1
print('Przewidywana wartość targetu: ' + str(p_svc.predict(obs)))
Przewidywana wartość targetu: [1]
Powyżej stworzyłem explainer dla naszego modelu.
Poniżej mamy informację, że bardzo ważną zmienną dla modelu jest thal_rd oraz thal_n ponieważ obie opisują to samo zjawisko ich wpływ można rozpatrywać wspólnie. Występownie wady odwracalnej jest wskaźnikiem choroby ponieważ oznacza, że zmiany pojawiły się niedawno.
Zmienna ca wskazująca na ilość zwapnionych narządów jest istotnym wskaźnikiem przeciw chorobie ponieważ, większość osób chorych przejawia wartości powyżej 0.
Warto zwrócić uwagę na niejednoznaczność mniennych cp. Badany mężczyzna wykazywał ból niezwiązany z dławicą (cp_np = 1) co wskazuje na brak choroby, jak również brak bólu dławicowego. Jednak zmienna cp_a = 0 oznaczająca brak niewystępownia bólu w ogóle, wzkazuję na chorobę co jest logiczną konsekwencją wcześniejszej wartości cp_np.
Już ze wcześniejszych analz naszego zbioru danych można było wnioskować, że zmeinne ca, thal, sex, exang oraz cp są tymi najważniejszymi dla predykcji modeli.
p2 = expl.predict_surrogate(obs, type='lime')
p2.show_in_notebook()
Postanowiłem wziąć dwie obserwacje które mają maksymalną i minimalną wartość predykcji explainera (index 13, 60).
Pierwsza jest to 51 letnia kobieta. Model z bardzo dużą pewnością zaklasyfikował ją jako zdrową, co jest prawdą. Jej wyniki wszystkich istotnych dla modelu badań są dobre. Oprócz restecg które wskazuje na przerost lewej komory serca ale wpływ tego czynnika jest minimalny jeżeli inne badania wskazują na brak choroby wieńcowej.
pr_ex = pd.DataFrame(data={'prediction' :expl.predict(x_test)})
#pr_ex.sort_values('prediction').iloc[28:40]
p31 = expl.predict_surrogate(x_test.iloc[13], type='lime')
p31.show_in_notebook()
Drugą obserwacją jest 67 letni mężczyzna posiadający chorobę wieńcową. Model ogromną pewnością przewiduję chorobę jedyną zminną która 'jest za' brakiem choroby jest thal_fd co jest spowodowane tym, że występuję już thal_rd który jest bardziej poważną zmianą mięśnia sercowego.
p32 = expl.predict_surrogate(x_test.iloc[60], type='lime')
p32.show_in_notebook()
Ponieważ powyższe obserwację pokrywają się z naszą intuicją i nie są ciekawe pod względem badania naszego modelu postanowiłem wziąć taką obserwację która została błędnie zdiagnozowana, a jej predykcja była blisko granicy decyzyjnej.
Jest to 52-letni mężczyzna. Który posiada chorobę wieńcową, a model zaklasyfikował go jako zdrowego. Opierając się na wynikach metody LIME, występuje tu parę czynników które mogły przyczynić się do tego błędu:
print('Rzeczywista wartość targetu: ' + str(y_test[42]))
Rzeczywista wartość targetu: 1
print('Przewidywana wartość targetu: ' + str(p_svc.predict(x_test.iloc[42:43])))
Przewidywana wartość targetu: [0]
p33 = expl.predict_surrogate(x_test.iloc[42], type='lime')
p33.show_in_notebook()